#! /usr/bin/env python
# -*- coding: iso-8859-1 -*-

# chimera - observatory automation system
# Copyright (C) 2006-2007  P. Henrique Silva <henrique@astro.ufsc.br>

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


from chimera.core.cli import ChimeraCLI, action, parameter
from chimera.core.callback import callback
from chimera.core.exceptions import ChimeraException

from chimera.interfaces.autofocus import StarNotFoundException, FocusNotFoundException
from chimera.interfaces.focuser import InvalidFocusPositionException

from chimera.util.ds9 import DS9

import sys
import time    

class ChimeraFocus (ChimeraCLI):
    
    def __init__ (self):
        ChimeraCLI.__init__(self, "chimera-focus", "Focuser controller", 0.1)
        
        self.addHelpGroup("FOCUS", "Focus")
        self.addInstrument(name="focuser", cls="Focuser", required=True, helpGroup="FOCUS", help="Focuser instrument to be used")
        self.addController(name="autofocus", cls="Autofocus", required=False, helpGroup="FOCUS", help="Autofocus controller to be used")
        
        self.addHelpGroup("AUTOFOCUS", "Autofocus")
        self.addParameters(dict(name="autofocus_step", long="step", type="int", helpGroup="AUTOFOCUS",
                                help="Defines autofocus step.", metavar="STEP", default=500),
                           dict(name="autofocus_exptime", long="exptime", type="float", helpGroup="AUTOFOCUS",
                                help="Defines autofocus frame exposure time.", metavar="EXPTIME", default=10.0),
                           dict(name="autofocus_debug", long="debug", helpGroup="AUTOFOCUS",
                                help="Run an autofocus debug session using data from PREVIOUS_RUN_DIR.",
                                metavar="PREVIOUS_RUN_DIR", default=""))

        self.addHelpGroup("COMMANDS", "Commands")        

    @parameter(long="range", helpGroup="AUTOFOCUS", default="1000-6000",
               help="Defines autofocus focuser range to be covered. Use start-end, "
               "as in 1000-6000 to run from 1000 to 6000.", metavar="START-END")
    def autofocus_range(self, value):
        try:
            start, end = value.split("-")
            start = int(start)
            end = int(start)
            return (start, end)
        except ValueError:
            raise ValuError("Invalid start-end range")
        
    @action(long="in", type="int", help="Move N steps IN", metavar="N", helpGroup="COMMANDS")
    def move_in (self, options):
        self.out("Moving %d steps IN ... " % options.move_in, end="")

        try:
            self.focuser.moveIn(options.move_in)
        except InvalidFocusPositionException, e:
            self.exit("Invalid position. Current position %d,"
                      " target position %d, valid range %d-%d." % (self.focuser.getPosition(),
                                                                   self.focuser.getPosition()-int(options.move_in),
                                                                   self.focuser.getRange()[0], self.focuser.getRange()[1]))

        self.out("OK")
        
        self._currentPosition(options)
        
    @action(long="out", type="int", help="Move N steps OUT", metavar="N", helpGroup="COMMANDS")
    def move_out (self, options):
        self.out("Moving %d steps OUT ... " % options.move_out, end="")

        try:
            self.focuser.moveOut(options.move_out)
        except InvalidFocusPositionException, e:
            self.exit("Invalid position. Current position %d,"
                      " target position %d, valid range %d-%d." % (self.focuser.getPosition(),
                                                                   self.focuser.getPosition()+int(options.move_out),
                                                                   self.focuser.getRange()[0], self.focuser.getRange()[1]))

        self.out("OK")
        
        self._currentPosition(options)

    @action(long="to", type="int",help="Move to POSITION", metavar="POSITION", helpGroup="COMMANDS")
    def move_to (self, options):
        self.out("Moving to %d ... " % options.move_to, end="")

        try:
            self.focuser.moveTo(options.move_to)
        except InvalidFocusPositionException, e:
            self.exit("Invalid position, must be between %d and %d," % self.focuser.getRange())

        self.out("OK")
        
    @action(short="i", help="Print focuser current information", helpGroup="COMMANDS")
    def info(self, options):
        
        drv = self.focuser.getDriver()
        
        self.out("Focuser: %s (%s)" % (self.focuser["driver"], drv["device"]))
        self._currentPosition(options)
        self.out("Valid range: %d-%d" % self.focuser.getRange())

    @action(helpGroup="AUTOFOCUS",
            help="Start an autofocus session using the selected parameters."
            " You can select a focuser range and the size of the step for the sequence."
            " This option is exclusive, you cannot move manually at the same time.")
    def auto(self, options):
        
        if not self.autofocus:
            self.exit("No Autofocus controller available. Try --autofocus=..., or --help.")

        ds9 = None
        try:
            ds9 = DS9(open=True)
        except IOError:
            pass

        @callback(self.localManager)
        def stepComplete (position, star, frame):

            self.out("#%04d (%4d, %4d) FWHM=%.3f FLUX=%.3f" % (position,
                                                               star["XWIN_IMAGE"], star["YWIN_IMAGE"],
                                                               star["FWHM_IMAGE"], star["FLUX_BEST"]))
                
            if ds9:
                ds9.displayFile(frame.filename())
                ds9.set("regions command { circle %d %d %d}" % (int(star["XWIN_IMAGE"]), int(star["YWIN_IMAGE"]), int(star["FWHM_IMAGE"])))
                #ds9.xpaset("pan to %d %d physical" % (int(star["XWIN_IMAGE"]), int(star["YWIN_IMAGE"])))
                        
        self.autofocus.stepComplete += stepComplete
        
        start, end = options.autofocus_range

        try:
            best_focus = self.autofocus.focus(exptime=options.autofocus_exptime,
                                              start=start, end=end, step=options.autofocus_step,
                                              minmax=(0,30), debug=False or options.autofocus_debug)
            print "Best focus position:", best_focus[-1][0]

        except IOError, e:
            self.exit("Invalid debug directory %s." % options.autofocus_debug)

        except FocusNotFoundException:
            self.exit("Couldn't find the best focus position."
                      " See autofocus log for more information.")
        except StarNotFoundException:
            self.exit("Couldn't find a suitable star to focus on.")

        time.sleep(1)

    def _currentPosition(self, options):
        return self.out("Current focuser position: %s" % self.focuser.getPosition())
    

def main():
    cli = ChimeraFocus()
    return cli.run(sys.argv)
    
if __name__ == '__main__':
    main()
